package com.github.cyf.util;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;

/**
 * @author chenyifan
 * @create 2024-05-24 18:22
 */
public class JacksonUtil {

    private static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";

    private static final String DATE_FORMAT = "yyyy-MM-dd";

    private static final String TIME_FORMAT = "HH:mm:ss";

    public static ObjectMapper MAPPER;

    static {

        MAPPER = new ObjectMapper();

        JavaTimeModule javaTimeModule = new JavaTimeModule();
        //处理LocalDateTime
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATETIME_PATTERN);
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));

        //处理LocalDate
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(dateFormatter));

        //处理LocalTime
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(TIME_FORMAT);
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(timeFormatter));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(timeFormatter));

        // 允许未转义的控制字符，在调用 configure() 方法时将其设置为启用即可。
        MAPPER.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(),true);
        // 设置 java.util.Date 格式： yyyy-MM-dd HH:mm:ss
        MAPPER.setDateFormat(new SimpleDateFormat(DATETIME_PATTERN));
        // 支持 JDK8 LocalDateTime、LocalDate、 LocalTime
        // Jdk8Module 模块支持如Stream、Optional等类
        MAPPER.registerModules(javaTimeModule, new Jdk8Module());
        // 序列化时包含所有字段
        MAPPER.setDefaultPropertyInclusion(JsonInclude.Include.ALWAYS);
        // 反序列化时,忽略在 json 字符串中存在, 但在 java 对象中不存在的属性
        MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 序列化时，日期/时间是否序列化为时间戳，这里禁止
        MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        // 允许忽略未知枚举值和通过 @JsonEnumDefaultValue 注释指定的预定义值的功能。
        // 如果禁用，未知的枚举值将引发异常。如果启用，但未指定预定义的默认 Enum 值，也会引发异常。
        MAPPER.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);
        // 在反序列化时接受不区分大小写的属性名。默认情况下，Jackson 在将 JSON 转换为 Java 对象时，会严格匹配属性名（包括大小写）。
        // 设置为 true 后，即使 JSON 中的属性名与 Java 对象中的字段名大小写不一致，Jackson 也会尝试找到匹配的字段并进行赋值。
        MAPPER.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
    }

    // -------------------------------------- 解析 json 字符串 -----------------------------------

    /**
     * json 字符串解析为对象
     * @param text json 字符串
     * @param clazz 对象类型
     */
    public static <T> T parseObject(String text, Class<T> clazz) {
        try {
            return MAPPER.readValue(text, clazz);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 字节数组解析为对象
     * @param bytes 字节数组
     * @param clazz 对象类型
     */
    public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
        try {
            return MAPPER.readValue(bytes, clazz);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * json 字符串解析为对象
     * @param text json 字符串
     * @param typeReference 类型引用
     */
    public static <T> T parseObject(String text, TypeReference<T> typeReference) {
        try {
            return MAPPER.readValue(text, typeReference);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * json 字符串解析为 map
     * @param text json 字符串
     */
    public static Map parseMap(String text) {
        try {
            return MAPPER.readValue(text, MAPPER.getTypeFactory().constructType(Map.class));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * json 字符串解析为列表
     * @param text json 字符串
     * @param clazz 列表元素类型
     */
    public static <T> List<T> parseArray(String text, Class<T> clazz) {
        try {
            return MAPPER.readValue(text, MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }




    // ----------------------------------------- 转换为 json 字符串 -----------------------------------

    /**
     * 对象转 json 字符串
     * @param object 对象
     * @return
     */
    public static String toJsonString(Object object) {
        try {
            return MAPPER.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 对象转 json 字符串，同时对 json 格式化
     * @param obj 对象
     * @return
     */
    public static String toFormatJsonString(Object obj) {
        try {
            return MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }


    // ------------------------------------------ json 树模型 -------------------------------------

    /**
     * json 字符串转换为 json 树
     * @param text
     * @return
     */
    public static JsonNode parseTree(String text) {
        try {
            return MAPPER.readTree(text);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 获取子对象，不存在的 key 会返回 null
     * 如果子对象是数组，node.for 去遍历
     * @param jsonObject json 对象
     * @param key 属性名称
     * @return
     */
    public static JsonNode getJSONObject(JsonNode jsonObject, String key) {
        key = key.startsWith("/") ? key : "/" + key;
        return jsonObject.at(key);
    }

    /**
     * 获取字符串属性，不存在的 key 会返回 null
     * @param jsonObject json 对象
     * @param key 属性名称
     * @return
     */
    public static String getString(JsonNode jsonObject, String key) {
        JsonNode node = getJSONObject(jsonObject, key);
        if (node == null) {
            return null;
        }
        return node.asText();
    }

    /**
     * 获取 int 属性，不存在的 key 会返回 null
     * @param jsonObject json 对象
     * @param key 属性名称
     * @return
     */
    public static Integer getInteger(JsonNode jsonObject, String key) {
        JsonNode node = getJSONObject(jsonObject, key);
        if (node == null) {
            return null;
        }
        return node.asInt();
    }

    /**
     * 获取 long 属性，不存在的 key 会返回 null
     * @param jsonObject json 对象
     * @param key 属性名称
     * @return
     */
    public static Long getLong(JsonNode jsonObject, String key) {
        JsonNode node = getJSONObject(jsonObject, key);
        if (node == null) {
            return null;
        }
        return node.asLong();
    }

    /**
     * 获取 double 属性，不存在的 key 会返回 null
     * @param jsonObject json 对象
     * @param key 属性名称
     * @return
     */
    public static Double getDouble(JsonNode jsonObject, String key) {
        JsonNode node = getJSONObject(jsonObject, key);
        if (node == null) {
            return null;
        }
        return node.asDouble();
    }

    /**
     * 获取 boolean 属性，不存在的 key 会返回 null
     * @param jsonObject json 对象
     * @param key 属性名称
     * @return
     */
    public static Boolean getBoolean(JsonNode jsonObject, String key) {
        JsonNode node = getJSONObject(jsonObject, key);
        if (node == null) {
            return null;
        }
        return node.asBoolean();
    }

    /**
     * 获取 Map 对象，不存在的 key 会返回 null
     * @param jsonObject json 对象
     * @param key 属性名称
     * @return
     */
    public static Map getMap(JsonNode jsonObject, String key) {
        JsonNode node = getJSONObject(jsonObject, key);
        if (node == null) {
            return null;
        }
        return MAPPER.convertValue(node, Map.class);
    }

    /**
     * 获取 bean 对象，不存在的 key 会返回 null
     * @param jsonObject json 对象
     * @param key 属性名称
     * @param clazz bean 类型
     * @return
     */
    public static <T> T getBean(JsonNode jsonObject, String key, Class<T> clazz) {
        JsonNode node = getJSONObject(jsonObject, key);
        if (node == null) {
            return null;
        }
        return MAPPER.convertValue(node, clazz);
    }

    /**
     * 获取 list 对象，不存在的 key 会返回 null
     * @param jsonObject json 对象
     * @param key 属性名称
     * @param clazz bean 列表元素类型
     * @return
     */
    public static <T> List<T> getList(JsonNode jsonObject, String key, Class<T> clazz) {
        JsonNode node = getJSONObject(jsonObject, key);
        if (node == null) {
            return null;
        }
        return MAPPER.convertValue(node, MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
    }

    /**
     * 获取数组，不存在的 key 会返回 null
     * @param jsonObject json 对象
     * @param key 属性名称
     * @param clazz bean 数组元素类型
     * @return
     */
    public static <T> T[] getArray(JsonNode jsonObject, String key, Class<T> clazz) {
        JsonNode node = getJSONObject(jsonObject, key);
        if (node == null) {
            return null;
        }
        return MAPPER.convertValue(node, MAPPER.getTypeFactory().constructArrayType(clazz));
    }




    // ---------------------------------------- Json 对象构建 -----------------------------------------

    /**
     * JsonNode是一个抽象类，不能实例化，创建JSON树形模型，得用JsonNode的子类ObjectNode
     * @return
     */
    public static ObjectNode newJSONObject() {
        return MAPPER.createObjectNode();
    }

    /**
     * 创建JSON数组对象
     * @return
     */
    public static ArrayNode newJSONArray() {
        return MAPPER.createArrayNode();
    }
}
